/*
 * keyhal.c
 *
 * Created: 8/16/2013 1:55:11 PM
 *  Author: Ken Arok
 *
 * \file
 *
 * \brief Hardware Abstraction Layer of Keypad Interface.
 *
 * Copyright (c) 2013 PT Hanindo Automation Solutions. All rights reserved.
 *
 */

#include "config_board.h"

#if BOARD_1_0_USED

#include "board.h"
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

#include "gpio.h"
#include "intc.h"
#include "eic.h"
#include "spi.h"

#include "delay.h"
#include "keyhal.h"

#define KEY_SPI_WR_ID			KEY_SPI_NPCS_WR
#define KEY_SPI_RD_ID			KEY_SPI_NPCS_RD
#define KEY_SPI_WR_BAUDRATE		4000000
#define KEY_SPI_RD_BAUDRATE		1000000

/* Keyword definition
   MSB [SC4 SC3 SC2 SC1 * 7 4 1 0 8 5 2 # 9 6 3] LSB
   0 -> pressed
   1 -> released
 */
#define KEYWORD_SHORTCUT4		0x7FFF
#define KEYWORD_SHORTCUT3		0xBFFF
#define KEYWORD_SHORTCUT2		0xDFFF
#define KEYWORD_SHORTCUT1		0xEFFF
#define KEYWORD_ASTERIC			0xF7FF
#define KEYWORD_NUMBER7			0xFBFF
#define KEYWORD_NUMBER4			0xFDFF
#define KEYWORD_NUMBER1			0xFEFF
#define KEYWORD_NUMBER0			0xFF7F
#define KEYWORD_NUMBER8			0xFFBF
#define KEYWORD_NUMBER5			0xFFDF
#define KEYWORD_NUMBER2			0xFFEF
#define KEYWORD_CROSS			0xFFF7
#define KEYWORD_NUMBER9			0xFFFB
#define KEYWORD_NUMBER6			0xFFFD
#define KEYWORD_NUMBER3			0xFFFE
#define KEYWORD_NONE			0xFFFF

/* MSB [SC4 SC3 SC2 SC1 * 7 4 1 0 8 5 2 # 9 6 3] LSB */
#define KEYWORD_COMBINE1		0xF111		//! * and 5 and 9
#define KEYWORD_COMBINE2		0xF444		//! 7 and 0 and 3
#define KEYWORD_COMBINE3		0xFAAA		//! 7 and 2 and 3
#define KEYWORD_COMBINE4		0xF1F1		//! * and 7 and 6
#define KEYWORD_COMBINE5		0xF4F4		//! * and 4 and 3
#define KEYWORD_COMBINE11		0xFEFE		//! 1 and 3
#define KEYWORD_COMBINE12		0xFFAA		//! 2 and 9
#define KEYWORD_COMBINE13		0xFF99		//! 6 and 8
#define KEYWORD_COMBINE14		0xFAAF		//! 1 and 8
#define KEYWORD_COMBINE15		0xF9F9		//! 6 and 7


/* Define type of keypad. */
#define KEY_BUTTON		1
#define KEY_TOUCH		0

/* Define 1 if SPI interface is used. Unless I2C is used (for key touch) */
#define KEY_SPI_IF_USED		1


/* Define external interrupt routine register for key. */
#define EXT_KEY_INT_IRQ		AVR32_EIC_IRQ_3


#if KEY_SPI_IF_USED

/* Keypad CS1 SPI characteristic options. */
static const spi_channel_options_t KEY_CS1_SPI_OPTIONS = {
	.baudrate = 4000000,	//!> 4 MHz clock frequency.
	.bits = 8,				//!> 8-bit data length.
	.delay_bs = 4,			//!> 4 x 30ns = 120ns TCES.
	.delay_bct = 2,			//!> 2 x 1us = 2us
	.keep_actv = 1,			//!> Drive CS logic manualy.
	.spi_mode = SPI_MODE_0,	//!> Mode 0 of SPI.
	.master_setup = true	//!> Setup in master mode.
};


static spi_descriptor_t spi_key_write_desc, spi_key_read_desc;

#else
#endif /* KEY_SPI_IF_USED. */

#if KEY_BUTTON

/* Keypad CS2 SPI characteristic options. */
static const spi_channel_options_t KEY_CS2_SPI_OPTIONS = {
	.baudrate = 1000000,	//!> 1 MHz clock frequency.
	.bits = 8,				//!> 8-bit data length.
	.delay_bs = 4,			//!> 4 x 30ns = 120ns TCES.
	.delay_bct = 5,			//!> 5 x 1us = 5us
	.keep_actv = 1,			//!> Drive CS logic manualy.
	.spi_mode = SPI_MODE_0,	//!> Mode 0 of SPI.
	.master_setup = true	//!> Setup in master mode.
};

#endif /* KEY_BUTTON */

xSemaphoreHandle xKeySignaling = NULL;

/** \brief Interrupt Service Routine if pressed key is detected.
 *
 * \return _xHigherPriorityTaskWoken	Pointer for task switching.
 *
 */
__attribute__((__noinline__)) static portBASE_TYPE prvEICKEY_ISR_NonNakedBehaviour(void)
{
	portBASE_TYPE _xHigherPriorityTaskWoken = pdFALSE;
	
	portENTER_CRITICAL();
	{
		gpio_toggle_pin(LED1_GPIO);
		eic_clear_interrupt_line(&AVR32_EIC, EXT_INT2);
		xSemaphoreGiveFromISR(xKeySignaling, &_xHigherPriorityTaskWoken);
	}
	portEXIT_CRITICAL();
	
	/* The return value will be used by portEXIT_SWITCHING_ISR() to know if it
	should perform a vTaskSwitchContext(). */
	return (_xHigherPriorityTaskWoken);	
}


/** \brief Interrupt Service Routine if pressed key is detected.
 */
__attribute__((__naked__)) static void prvKEY_ISR(void)
{
	/* This ISR can cause a context switch, so the first statement must be a
	call to the portENTER_SWITCHING_ISR() macro.  This must be BEFORE any
	variable declarations. */
	portENTER_SWITCHING_ISR();

	prvEICKEY_ISR_NonNakedBehaviour();

	/* Exit the ISR.  If a task was woken by either a character being received
	or transmitted then a context switch will occur. */
	portEXIT_SWITCHING_ISR();	
}


#if KEY_BUTTON

/** \brief Scan the pressed key button.
 *
 * \return KEYWORD		16 bit keyword representative for pressed button.
 *
 */
static uint16_t _scan_button(void) {
	const uint8_t _col_val[4] = {0x01, 0x02, 0x04, 0x08};
	uint8_t _rxKeyByte, _idle_port = 0x00;
	uint16_t _button_val = 0x0000;
	uint16_t _dummy16;
	
	/* Scan column 0. */
	gpio_set_pin_high(KEY_LOAD_GPIO);
	while(SPI_STATUS_OK != spi_select_chip(spi_key_write_desc));
	spi_write_byte(spi_key_write_desc, (const void *)&_col_val[0], 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_write_desc));
	/* Load Input. */
	gpio_set_pin_low(KEY_LOAD_GPIO);
	delay_us(10);
	gpio_set_pin_high(KEY_LOAD_GPIO);
	delay_us(1);
	/* Read Input. */
	while(SPI_STATUS_OK != spi_select_chip(spi_key_read_desc));
	spi_read_byte(spi_key_read_desc, (void *)&_rxKeyByte, 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_read_desc));
	_dummy16 = (uint16_t)(_rxKeyByte >> 1) & 0x000F;
	_dummy16 = _dummy16 << 12;
	_button_val = _button_val | _dummy16;
	
	/* Scan column 1. */
	gpio_set_pin_high(KEY_LOAD_GPIO);
	while(SPI_STATUS_OK != spi_select_chip(spi_key_write_desc));
	spi_write_byte(spi_key_write_desc, (const void *)&_col_val[1], 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_write_desc));
	/* Load Input. */
	gpio_set_pin_low(KEY_LOAD_GPIO);
	delay_us(10);
	gpio_set_pin_high(KEY_LOAD_GPIO);
	delay_us(1);
	/* Read Input. */
	while(SPI_STATUS_OK != spi_select_chip(spi_key_read_desc));
	spi_read_byte(spi_key_read_desc, (void *)&_rxKeyByte, 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_read_desc));
	_dummy16 = (uint16_t)(_rxKeyByte >> 1) & 0x000F;
	_dummy16 = _dummy16 << 8;
	_button_val = _button_val | _dummy16;
	
	/* Scan column 2. */
	gpio_set_pin_high(KEY_LOAD_GPIO);
	while(SPI_STATUS_OK != spi_select_chip(spi_key_write_desc));
	spi_write_byte(spi_key_write_desc, (const void *)&_col_val[2], 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_write_desc));
	/* Load Input. */
	gpio_set_pin_low(KEY_LOAD_GPIO);
	delay_us(10);
	gpio_set_pin_high(KEY_LOAD_GPIO);
	delay_us(1);
	/* Read Input. */
	while(SPI_STATUS_OK != spi_select_chip(spi_key_read_desc));
	spi_read_byte(spi_key_read_desc, (void *)&_rxKeyByte, 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_read_desc));
	_dummy16 = (uint16_t)(_rxKeyByte >> 1) & 0x000F;
	_dummy16 = _dummy16 << 4;
	_button_val = _button_val | _dummy16;
	
	/* Scan column 3. */
	gpio_set_pin_high(KEY_LOAD_GPIO);
	while(SPI_STATUS_OK != spi_select_chip(spi_key_write_desc));
	spi_write_byte(spi_key_write_desc, (const void *)&_col_val[3], 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_write_desc));
	/* Load Input. */
	gpio_set_pin_low(KEY_LOAD_GPIO);
	delay_us(10);
	gpio_set_pin_high(KEY_LOAD_GPIO);
	delay_us(1);
	/* Read Input. */
	while(SPI_STATUS_OK != spi_select_chip(spi_key_read_desc));
	spi_read_byte(spi_key_read_desc, (void *)&_rxKeyByte, 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_read_desc));
	_dummy16 = (uint16_t)(_rxKeyByte >> 1) & 0x000F;
	_dummy16 = _dummy16 << 0;
	_button_val = _button_val | _dummy16;
	
	/* Make sure all Column key are low. */
	while(SPI_STATUS_OK != spi_select_chip(spi_key_write_desc));
	spi_write_byte(spi_key_write_desc, (const void *)&_idle_port, 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_write_desc));

	return _button_val;
	
}

#endif /* KEY_BUTTON */

/* ------------------------------------------------------------------------------------------------ */

void key_disable(void)
{
	eic_disable_line(&AVR32_EIC, EXT_INT2);
	eic_disable_interrupt_line(&AVR32_EIC, EXT_INT2);
}

void key_enable(void)
{
	eic_enable_line(&AVR32_EIC, EXT_INT2);
	eic_enable_interrupt_line(&AVR32_EIC, EXT_INT2);
}

void key_init(void)
{
	uint8_t _dummy = 0;
#if KEY_BUTTON
	const char _spi_key_read_port[] = "SPIKEY_R";
#endif

#if KEY_BUTTON || KEY_SPI_IF_USED
	const char _spi_key_write_port[] = "SPIKEY_W";
#endif
	
	/** \TODO: Although Key Read SPI (CS2) is set to Mode 0 but the waveform show it at mode 2 or mode 1.
			  Thus, read key value must be modified (shift right one step). Need to be fix next time.
	 */
#if KEY_BUTTON || KEY_SPI_IF_USED
	spi_key_write_desc = spi_open(_spi_key_write_port, 0);
	if(spi_key_write_desc) {
		spi_setup(spi_key_write_desc, &KEY_CS1_SPI_OPTIONS);
	}
#endif

#if KEY_BUTTON
	spi_key_read_desc = spi_open(_spi_key_read_port, 0); {
		spi_setup(spi_key_read_desc, &KEY_CS2_SPI_OPTIONS);
	}
#endif
	
	/* Register the EIC interrupt handler to the interrupt controller. */
	INTC_register_interrupt((__int_handler)&prvKEY_ISR, EXT_KEY_INT_IRQ, AVR32_INTC_INT0);
	
	while(SPI_STATUS_OK != spi_select_chip(spi_key_write_desc));
	spi_write_byte(spi_key_write_desc, (const void *)&_dummy, 1);
	while(SPI_STATUS_OK != spi_deselect_chip(spi_key_write_desc));
	
	/* Create Key Signaling. */
	if(xKeySignaling == NULL) {
		vSemaphoreCreateBinary(xKeySignaling);
		if(!xKeySignaling) while(1);
	}
	
	/* Keypad idle. */
	xSemaphoreTake(xKeySignaling, (portTickType)50);
}


uint16_t key_get_word(void)
{
	uint16_t _key_ver1;

	key_disable();

#if KEY_BUTTON 	
	delay_ms(20);
	_key_ver1 = _scan_button();
	delay_ms(1);
#endif /* KEY_BUTTON  */
	
	key_enable();

	return _key_ver1;
	
}

char key_get_char(void)
{
	uint16_t keyword;
	char retkey;
	
	keyword = key_get_word();
	
	retkey = 0xFF;
	
	switch(keyword)
	{
		case KEYWORD_NUMBER0:
			retkey = '0';
		break;
		
		case KEYWORD_NUMBER1:
			retkey = '1';
		break;

		case KEYWORD_NUMBER2:
			retkey = '2';
		break;

		case KEYWORD_NUMBER3:
			retkey = '3';
		break;

		case KEYWORD_NUMBER4:
			retkey = '4';
		break;

		case KEYWORD_NUMBER5:
			retkey = '5';
		break;

		case KEYWORD_NUMBER6:
			retkey = '6';
		break;

		case KEYWORD_NUMBER7:
			retkey = '7';
		break;

		case KEYWORD_NUMBER8:
			retkey = '8';
		break;
		
		case KEYWORD_NUMBER9:
			retkey = '9';
		break;

		case KEYWORD_ASTERIC:
			retkey = '*';
		break;

		case KEYWORD_CROSS:
			retkey = '#';
		break;
		
		default: break;

	}
	
	return retkey;	
}


uint16_t key_get_combine(void)
{
	uint16_t _kCombine;

	_kCombine = key_get_word();
	/* Bounce countermeasure. */
	delay_ms(200);

	return _kCombine;
}


char key_wait_keychar(const char *_keychar)
{
	char keychar;
	uint8_t i;
	
	if(_keychar == NULL) return 0;
	
	while(1) {
		keychar = key_get_char();
		i = 0;
		do {
			if(keychar == _keychar[i]) {
				return keychar;
			}
			i++;
		}while(_keychar[i]);
		
	}
	
	return 0;
}

void key_wait_release(void)
{
	while(key_get_word() != 0xFFFF);
}

#endif /* BOARD_1_0_USED */
